/*
    The NeonX Library

    Copyright (c) 2013 darkcat

    Code covered by the MIT License
*/

#pragma once

// nx...
#include "config/general.h"
// nx_operator
#include "utility/operator.h"
// nx_foreach
#include "algorithm/foreach.h"

//////////////////////////////////////////////////////////////////////////
NX_BEG
//////////////////////////////////////////////////////////////////////////

template <typename Model_>
struct Iterate : public Model_, nx_operator(Iterate<Model_>, Operable)
{
    typedef typename Model_::type_t type_t;

public:
    Iterate(const int& v = 0)
    {
        Model_::_(v);
    }

    Iterate& operator+=(const int& v)
    {
        Model_::_(v);
        return (*this);
    }

    type_t val(void) const
    {
        return Model_::val;
    }

    type_t operator*(void) const { return val(); }
};

//////////////////////////////////////////////////////////////////////////
// Series of numbers

template <typename T>
struct SeriesBase
{
    T val, prv;
    SeriesBase() : val(0), prv(0) {}

    typedef T type_t;
};

/*
    Accumulation (N * (n - 1))

    -->
    0 : 0
    n : N + f(n - 1)
*/

template <typename T, size_t N>
struct by_acc : SeriesBase<T>
{
    typedef SeriesBase<T> base_t;

    void _(int n)
    {
        if (n == 0) return;
        if (n > 0)
        {
            nx_foreach(i, n)
            {
                base_t::prv = base_t::val;
                base_t::val += N;
            }
        }
        else
        {
            nx_foreach(i, n)
            {
                base_t::val = base_t::prv;
                base_t::prv -= N;
            }
        }
    }
};

/*
    (N ^ (n - 1))

    -->
    0 : 0
    1 : 1
    n : N * f(n - 1)
*/

template <typename T, size_t N>
struct by_powerof : SeriesBase<T>
{
    typedef SeriesBase<T> base_t;

    void _(int n)
    {
        if (n == 0) return;
        if (n > 0)
        {
            nx_foreach(i, n)
            {
                base_t::prv = base_t::val;
                if (base_t::prv == 0)
                    base_t::val = 1;
                else
                    base_t::val *= N;
            }
        }
        else
        {
            nx_foreach(i, n)
            {
                base_t::val = base_t::prv;
                base_t::prv /= N;
            }
        }
    }
};

/*
    Fibonacci

    -->
    0 : 0
    1 : 1
    n : f(n - 1) + f(n - 2)
*/

template <typename T, size_t N = 1>
struct by_fibonacci : SeriesBase<T>
{
    typedef SeriesBase<T> base_t;

    void _(int n)
    {
        if (n == 0) return;
        if (n > 0)
        {
            nx_foreach(i, n)
            {
                if (base_t::val == 0)
                {
                    base_t::val = N;
                    base_t::prv = 0;
                }
                else
                {
                    T tmp = base_t::val;
                    base_t::val += base_t::prv;
                    base_t::prv = tmp;
                }
            }
        }
        else
        {
            nx_foreach(i, n)
            {
                T tmp = base_t::prv;
                base_t::prv = (base_t::val -= base_t::prv);
                base_t::val = tmp;
            }
        }
    }
};

//////////////////////////////////////////////////////////////////////////
NX_END
//////////////////////////////////////////////////////////////////////////
